package com.yunti.springboot.config.exception;

import com.google.gson.Gson;
import com.yunti.springboot.config.log.ApiLogConfig;
import com.yunti.springboot.config.log.ExceptionApiLog;
import com.yunti.springboot.mapper.ApiLogMapper;
import com.yunti.springboot.po.ApiLog;
import com.yunti.springboot.vo.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.Objects;

/**
 * @author Administrator
 */
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler implements ResponseBodyAdvice<Object> {
    @Resource
    ApiLogMapper mapper;
    @Autowired
    protected HttpServletRequest request;
    private final static String EXCEPTION_MSG_KEY = "Exception message : ";

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    /**
     * 此方法是拦截返回值,并且操作返回值的,这是一个全局过滤
     */
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
                                  ServerHttpResponse response) {
        ServletServerHttpRequest req = (ServletServerHttpRequest) request;
        HttpServletRequest servletRequest = req.getServletRequest();
        ApiLog a = (ApiLog) servletRequest.getAttribute(ApiLogConfig.APILOG_OBJ_SAVE_KEY);
        try {
            if (a != null && body != null) {
                ApiLogConfig.setLog(servletRequest, a.setResString(new Gson().toJson(body)));
                servletRequest.setAttribute(ApiLogConfig.APILOG_OBJ_SAVE_KEY, a.setResString(new Gson().toJson(body)));
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return body;
    }
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Result getExcept(Exception e) {
        ApiLog a = (ApiLog) request.getAttribute(ApiLogConfig.APILOG_OBJ_SAVE_KEY);

        if (a != null && e != null) {
            new Thread(() -> {
                try {
                    a.setEndTime(new Date());
                    a.setResCode("500");
                    a.setCostTime(a.getEndTime().getTime() - a.getStartTime().getTime());
                    a.setResString(new Gson().toJson(new ExceptionApiLog(e.getMessage(), ExceptionApiLog.getExceptionSrintStackTrace(e))));
                    mapper.insert(a);
                } catch (Exception exception) {
                    log.error(exception.getMessage());
                }
            }).start();
        }
        log.error("异常：{}", e);
        return Result.error(e.getMessage());
    }
    @ResponseBody
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handleValidException(MethodArgumentNotValidException e){
        //日志记录错误信息
        log.error(Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage());
        //将错误信息返回给前台
        return Result.error("103", Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage());
    }

    @ResponseBody
    @ExceptionHandler(NumberFormatException.class)
    public Result handleValidException(NumberFormatException e){
        log.error(e.toString());
        //将错误信息返回给前台
        return Result.error("103","数据格式错误", e);
    }
}
